﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

using System;
using System.Reflection;
namespace SG
{
    public class UnityUtils
    {
        /// <summary>
        /// 搜索子物体组件-Component版
        /// </summary>
        public static T Get<T>(Component go, string subnode) where T : Component
        {
            return go.transform.Find(subnode).GetComponent<T>();
        }

        /// <summary>
        /// 添加新的GameObject到父节点
        /// </summary>
        static public GameObject AddChild(GameObject parent)
        {
            GameObject go = new GameObject();
            if (parent != null)
            {
                go.transform.SetParent(parent.transform, false);
                go.layer = parent.layer;
            }
            return go;
        }

        /// <summary>
        /// 实例化prefab到父节点
        /// </summary>
        static public GameObject AddChild(GameObject parent, GameObject prefab)
        {
            GameObject go = GameObject.Instantiate(prefab) as GameObject;
            if (go != null && parent != null)
            {
                go.transform.SetParent(parent.transform, false);
                go.layer = parent.layer;
            }
            return go;
        }
        /// <summary>
        /// 添加特定组件的GameObject到父节点
        /// </summary>
        static public T AddChild<T>(GameObject parent)
            where T : Component
        {
            GameObject go = AddChild(parent);
            string name = typeof(T).ToString();
            go.name = name.Substring(name.LastIndexOf(".") + 1);
            return go.AddComponent<T>();
        }

        /// <summary>
        /// 清除所有子节点
        /// </summary>
        public static void ClearChild(Transform go)
        {
            if (go == null) return;
            for (int i = go.childCount - 1; i >= 0; i--)
            {
                GameObject.Destroy(go.GetChild(i).gameObject);
            }
        }

        /// <summary>
        /// 添加组件,保持只有一个该组件
        /// </summary>
        public static T Add<T>(GameObject go) where T : Component
        {
            if (go != null)
            {
                T[] ts = go.GetComponents<T>();
                for (int i = 0; i < ts.Length; i++)
                {
                    if (ts[i] != null) GameObject.Destroy(ts[i]);
                }
                return go.gameObject.AddComponent<T>();
            }
            return null;
        }

        /// <summary>
        /// 添加组件
        /// </summary>
        public static T Add<T>(Transform go) where T : Component
        {
            return Add<T>(go.gameObject);
        }

        /// <summary>
        /// 搜索子物体组件-GameObject版
        /// </summary>
        public static T Get<T>(GameObject go, string subnode) where T : Component
        {
            if (go != null)
            {
                Transform sub = go.transform.Find(subnode);
                if (sub != null) return sub.GetComponent<T>();
            }
            return null;
        }

        /// <summary>
        /// 搜索子物体组件-Transform版
        /// </summary>
        public static T Get<T>(Transform go, string subnode) where T : Component
        {
            if (go != null)
            {
                Transform sub = go.Find(subnode);
                if (sub != null) return sub.GetComponent<T>();
            }
            return null;
        }

        /// <summary>
        /// 取平级对象
        /// </summary>
        public static GameObject Peer(GameObject go, string subnode)
        {
            return Peer(go.transform, subnode);
        }

        /// <summary>
        /// 取平级对象
        /// </summary>
        public static GameObject Peer(Transform go, string subnode)
        {
            Transform tran = go.parent.Find(subnode);
            if (tran == null) return null;
            return tran.gameObject;
        }

        /// <summary>
        /// 销毁对象，在Editor下，采用DestroyImmediate方法，运行时采用Destroy方法。
        /// </summary>
        static public void Destroy(UnityEngine.Object obj,float delay = 0.0f)
        {
#if UNITY_EDITOR
            if (Application.isPlaying)
            {
                if (delay <= 0f)
                {
                    UnityEngine.Object.Destroy(obj);
                }
                else
                {
                    UnityEngine.Object.Destroy(obj, delay);
                }
            }
            else
            {
                UnityEngine.Object.DestroyImmediate(obj);
            }
#else
			UnityEngine.Object.Destroy(obj);
#endif
        }        
        
        public static GameObject NewGameObject(string name, Transform parent = null)
        {
            GameObject gobj = new GameObject(name);
            IdentityTransform(parent, gobj.transform);
            return gobj;
        }

        public static GameObject UniqueNewGameObject(string name)
        {
            GameObject gobj = GameObject.Find(name);
            if (null == gobj)
            {
                gobj = new GameObject(name);
            }

            IdentityTransform(null, gobj.transform);
            return gobj;
        }

        static public void RemoveComponent<T>(GameObject gobj) where T : Component
        {
            T t = gobj.GetComponent<T>() as T;
            if (null != t)
            {
                Destroy(t);
            }
        }

        public static Component AddComponent(GameObject go, string assembly, string classname)
        {
            Assembly asmb = Assembly.Load(assembly);
            Type t = asmb.GetType(assembly + "." + classname);
            return go.AddComponent(t);
        }

        public static Component AddComponentUnique(GameObject gobj, Type type)
        {
            Component t = gobj.GetComponent(type);
            if (null == t)
            {
                t = gobj.AddComponent(type);
            }
            else
            {
                if (t is Behaviour && !(t as Behaviour).enabled)
                {
                    (t as Behaviour).enabled = true;
                }
            }

            return t;
        }
        public static T AddComponentUnique<T>(GameObject gobj) where T : Component
        {
            Type t = typeof(T);
            return AddComponentUnique(gobj,t)as T;
        }


        /// <summary>
        /// Finds the specified component on the game object or one of its parents.
        /// </summary>

        static public T FindInParents<T>(GameObject go) where T : Component
        {
            if (go == null) return null;
            // Commented out because apparently it causes Unity 4.5.3 to lag horribly:
            // http://www.tasharen.com/forum/index.php?topic=10882.0
            //#if UNITY_4_3
#if UNITY_FLASH
		object comp = go.GetComponent<T>();
#else
            T comp = go.GetComponent<T>();
#endif
            if (comp == null)
            {
                Transform t = go.transform.parent;

                while (t != null && comp == null)
                {
                    comp = t.gameObject.GetComponent<T>();
                    t = t.parent;
                }
            }
#if UNITY_FLASH
		return (T)comp;
#else
            return comp;
#endif
        }
        
        public static List<Transform> GetChilds(UnityEngine.Transform parent)
        {
            List<Transform> list = new List<Transform>();
            if (null == parent || parent.childCount == 0)
            {
                return list;
            }

            List<Transform> cList = null;
            foreach (Transform child in parent)
            {
                list.Add(child);

                cList = GetChilds(child);
                if (null == cList || cList.Count == 0)
                {
                    continue;
                }
                list.AddRange(cList);
                cList.Clear();
            }
            return list;
        }

        /// <summary>
        /// 查找子对象
        /// </summary>
        public static GameObject GetChild(GameObject go, string subnode)
        {
            return GetChild(go.transform, subnode).gameObject;
        }

        public static Transform GetChild(UnityEngine.Transform parent, string name)
        {
            if (null == parent || parent.childCount == 0)
            {
                return null;
            }

            Transform result = null;
            foreach (Transform child in parent)
            {
                if (child.name.Equals(name))
                {
                    result = child;
                    break;
                }

                result = GetChild(child, name);
                if (null != result)
                {
                    break;
                }
            }

            return result;
        }

        public static T GetChildComponent<T>(Transform parent, string name) where T : Component
        {
            Transform child = GetChild(parent, name);
            if (null != child)
            {
                return child.GetComponent<T>();
            }
            return null;
        }

        /// <summary>
        /// 子物体隐藏与显示
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="noHideName"></param>
        static public void HideChildrenExclusive(UnityEngine.Transform parent, string noHideName)
        {
            foreach (Transform child in parent)
            {
                child.gameObject.SetActive(child.name.Equals(noHideName));
            }
        }

        /// <summary>
        /// 子物体隐藏与显示
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="noHideName"></param>
        static public void HideChildrenExclusive(UnityEngine.Transform parent, params Transform[] children)
        {
            if (null == children || children.Length == 0)
            {
                return;
            }

            for (int i = 0; i < parent.childCount; i++)
            {
                Transform child = parent.GetChild(i);
                int index = System.Array.FindIndex<Transform>(children, delegate (Transform match)
                {
                    return match == child;
                });

                child.gameObject.SetActive(index != -1);
            }
        }

        static public void IdentityTransform(Transform parent, Transform target, Vector3 localPosition, bool checkParentChanged = true)
        {
            bool parentChanged = checkParentChanged && (parent != target.parent);
            target.parent = parent;
            target.localPosition = localPosition;
            target.localRotation = Quaternion.identity;
            target.localScale = Vector3.one;
            if (parentChanged)
            {
#if USE_NGUI
            UIWidget[] uiWidgets = target.GetComponentsInChildren<UIWidget>();
            if (uiWidgets.Length > 0)
            {
                for (int i = uiWidgets.Length - 1; i >= 0; --i)
                {
                    uiWidgets[i].ParentHasChanged();
                }
            }
#endif
            }
        }

        static public void IdentityTransform(Transform parent, Transform target, bool checkParentChanged = true)
        {
            IdentityTransform(parent, target, Vector3.zero, checkParentChanged);
        }

        public static bool IsParent(Transform child, Transform parent)
        {
            if (null != child && null != parent)
            {
                Transform tran = child.parent;
                while (null != tran)
                {
                    if (tran == parent)
                    {
                        return true;
                    }
                    tran = tran.parent;
                }
            }
            return false;
        }

        static public Quaternion LookRotation(Vector3 forward)
        {
            if (forward.sqrMagnitude <= 0f)
            {
                //Debug.LogWarning("Look rotation viewing vector is zero!");
                return Quaternion.identity;
            }
            return Quaternion.LookRotation(forward);
        }

        static public void SetRotation(Transform tran, Vector3 forward)
        {
            if (null == tran) return;

            if (Mathf.Abs(forward.x) <= 0.001f
                && Mathf.Abs(forward.y) <= 0.001f
                && Mathf.Abs(forward.z) <= 0.001f)
            {
                //Debug.LogWarning("Look rotation viewing vector is zero!");
                return;
            }

            tran.forward = forward;
        }

        static public string GetWithoutClone(string src)
        {
            if (!src.Contains("(Clone)"))
            {
                return src;
            }
            return src.Replace("(Clone)", string.Empty);
        }

        static public object ScriptableDeepClone(object other)
        {
            return ReflectionUtils.DeepClone(other);
        }

        static public bool IsScriptableObject(System.Type type)
        {
            if (!type.IsClass)
                return false;

            bool result = type.IsSubclassOf(typeof(ScriptableObject));
            return result;
        }

        static public object CloneScriptableObject(object other)
        {
            System.Type thisType = other.GetType();
            object newClone = ScriptableObject.CreateInstance(thisType);

            System.Type tmpType = thisType;
            if (tmpType != null)
            {
                FieldInfo[] fieldInfos = tmpType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                foreach (FieldInfo field in fieldInfos)
                {
                    object val = field.GetValue(other);

                    // 如果一个类中声明了同样类型的变量，且不为空，直接赋值，防止死循环
                    if (null != val && val.GetType() == tmpType)
                    {
                        object val1 = val;
                        field.SetValue(newClone, val1);
                    }
                    else
                    {
                        object val1 = ScriptableDeepClone(val);
                        field.SetValue(newClone, val1);
                    }
                }

                PropertyInfo[] propInfos = tmpType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                foreach (PropertyInfo current in propInfos)
                {
                    if (current.CanRead && current.CanWrite)
                    {
                        ParameterInfo[] paramInfos = current.GetIndexParameters();
                        if (paramInfos.Length == 0)
                        {
                            object val = current.GetValue(other, null);

                            // 如果一个类中声明了同样类型的变量，且不为空，直接赋值，防止死循环
                            if (null != val && val.GetType() == tmpType)
                            {
                                //DebugLogger.Print("val:", val.GetType());
                                object val1 = val;
                                current.SetValue(newClone, val1, null);
                            }
                            else
                            {
                                object val1 = ScriptableDeepClone(val);
                                current.SetValue(newClone, val1, null);
                            }
                        }
                        else
                        {
                            // 此处逻辑不处理
                        }
                    }
                }
                //tmpType = tmpType.BaseType;
            }

            return newClone;
        }

        static public void EnableRenderers(Transform transform, bool enable)
        {
            if (transform == null)
            {
                return;
            }

            Renderer[] renderers = transform.GetComponentsInChildren<Renderer>();
            if (renderers != null)
            {
                foreach (Renderer r in renderers)
                {
                    r.enabled = enable;
                }
            }
        }

        public static T GetComponetByCollider<T>(Collider collider) where T : Component
        {
            if (null == collider) return null;
            return GetComponetByObject<T>(collider.gameObject);
        }

        public static T GetComponetByObject<T>(Transform tran) where T : Component
        {
            if (null == tran) return null;
            return GetComponetByObject<T>(tran.gameObject);
        }

        public static T GetComponetByObject<T>(GameObject gobj, bool includeInactive = false) where T : Component
        {
            if (null == gobj) return null;

            UnityEngine.Profiling.Profiler.BeginSample("GetComponetByObject");
            T unit = gobj.GetComponent<T>();
            if (null != unit)
            {
                UnityEngine.Profiling.Profiler.EndSample();
                return unit;
            }

            if (includeInactive)
            {
                T[] comps = gobj.GetComponentsInParent<T>(includeInactive);
                if (comps.Length > 0)
                {
                    unit = comps[0];
                }
            }
            else
            {
                unit = gobj.GetComponentInParent<T>();
            }
            if (null != unit)
            {
                UnityEngine.Profiling.Profiler.EndSample();
                return unit;
            }

            if (includeInactive)
            {
                T[] comps = gobj.GetComponentsInChildren<T>(includeInactive);
                if (comps.Length > 0)
                {
                    unit = comps[0];
                }
            }
            else
            {
                unit = gobj.GetComponentInChildren<T>();
            }
            UnityEngine.Profiling.Profiler.EndSample();
            return unit;
        }


        static public RenderTexture CreateRTTexture(int width, int height)
        {
            RenderTexture rtt = RenderTexture.GetTemporary(width, height, 16, RenderTextureFormat.ARGB32);
            rtt.filterMode = FilterMode.Point;
            rtt.hideFlags = HideFlags.HideAndDontSave;

            if (!rtt.IsCreated())
            {
                rtt.Create();
            }
            //mTargetTexture.MarkRestoreExpected();
            return rtt;
        }
        
        static public void SetSpriteRenderQueue(GameObject gobj, int queue)
        {
            if (null == gobj)
            {
                return;
            }

            SpriteRenderer[] renders = gobj.GetComponentsInChildren<SpriteRenderer>();
            foreach (SpriteRenderer render in renders)
            {
                render.material.renderQueue = queue;
            }
        }

        static public void SetParticleRenderQueue(GameObject gobj, int queue)
        {
            if (null == gobj)
            {
                return;
            }

            ParticleSystem[] particles = gobj.GetComponentsInChildren<ParticleSystem>();
            foreach (ParticleSystem par in particles)
            {
                par.GetComponent<Renderer>().material.renderQueue = queue;
            }
        }

        static public void StopParticle(Transform t)
        {
            ParticleSystem particle = t.GetComponent<ParticleSystem>();
            if (particle != null)
            {
                particle.Stop(true);
            }

            Animation animation = t.GetComponent<Animation>();
            if (animation != null)
            {
                animation.Stop();
            }

            for (int i = 0, imax = t.childCount; i < imax; ++i)
            {
                Transform child = t.GetChild(i);
                StopParticle(child);
            }
        }
        
        /// <summary>
        /// 查询最顶端父节点
        /// </summary>
        /// <param name="go"></param>
        /// <returns></returns>
        static public GameObject FindTopParent(GameObject go)
        {
            if (go == null)
                return null;

            Transform trans = go.transform;
            Transform child = trans;

            if (trans.parent == null)
                return child.gameObject;

            Transform parent = child.parent;
            while (null != parent)
            {
                child = parent;
                parent = child.parent;
            }

            return child.gameObject;
        }

        public static bool IsInScreen(Transform t)
        {
            return IsInScreen(t.position);
        }

        public static bool IsInScreen(Vector3 p)
        {
            return Mathf.Abs(p.x) < Screen.width / 2 && Mathf.Abs(p.y) < Screen.height / 2;
        }
    }
}